#!/usr/bin/env python

import threading

class DataTransform:
    def _get_function(self):
        return self.__function
    def _set_function(self, function):
        self.__function = function
        
    def _get_inputs(self):
        return self.__inputs
    def _get_outputs(self):
        return self.__outputs
    
    def _get_status(self):
        return self.__status
    def _set_status(self, status):
        self.__status = status
        
    def _get_name(self):
        return self.__name
    def _set_name(self, name):
        self.__name = name
        
    def _get_dataport(self):
        return self.__dataport
    def _set_dataport(self, dataport):
        self.__dataport = dataport
    
    inputs = property(_get_inputs)
    outputs = property(_get_outputs)
    function = property(_get_function, _set_function)
    status = property(_get_status, _set_status)
    name = property(_get_name, _set_name)
    dataport = property(_get_dataport, _set_dataport)
    
    
    def __init__(self, dataport, name = "DataTransform", function = None):
        self.status = DataTransformStatus.Stopped
        self.name = name
        self.__inputs = dict()
        self.__outputs = dict()
        self.function = function
        self.worker_thread = None
        self.dataport = dataport
    
    def transform(self):
        self.status = DataTransformStatus.Busy
        # call _transform_core() on another thread
        
        threading.Thread(target=self._transform_core, args=(self)).start()
        

    def _transform_core(self):
        if (self.function is not None):
            # Create initial dict of values retrieved from the inputs' values
            inputValues = self._get_input_values()
            outputValues = dict()
            for key in self.outputs:
                outputValues[key] = None
            
            self.function(self, inputValues, outputValues)

        self._on_transform_complete(outputValues)

    def _on_transform_complete(self, outputValues):
        self.prepare_outputs(outputValues)

        self.Status = DataTransformStatus.Completed
        _inform_awaiting_inputs()

    def _prepare_outputs(self, outputValues):
        for output in self.outputs:
            if (output.name in outputValues):
                output.value = outputValues[output.Name]
            else:
                output.value = None

    def _inform_awaiting_inputs(self):
        for output in self.outputs:
            for transformInput in output.registered_inputs:
                transformInput._on_output_ready(output)

    def _get_input_values(self):
        values = dict()
        for key in self.Inputs.keys():
            values[key] = self.Inputs[key].Source.Value
        return values
    
    def load_file(self, filePath):
        pass
    
    def load_xml(self, xmlDom):
        
        
        
        pass
    
    def save_file(self, filePath):
        pass
    
    def save_xml(self):
        pass

class DataTransformStatus:
    Busy = "Busy"
    Finished = "Finished"
    Waiting = "Waiting"
    Stopped = "Stopped"

class DataTransformInput:
    def _get_name(self):
        return self.__name
    def _set_name(self, name):
        self.__name = name
    def _get_transform(self):
        return self.__transform
    def _set_transform(self, transform):
        self.__transform = transform
        if (not transform == None):
            transform.Inputs[self.Name] = self
        
    def _get_source(self):
        return self.__source

    def _set_source(self, source):
        if (self.__source is not None):
            self.__source._unregister(self)
        self.__source = source
        if (self.__source is not None):
            self.__source._register(self)
        
    name = property(_get_name, _set_name)
    transform = property(_get_transform, _set_transform)
    source = property(_get_source, _set_source)
    
    def __init__(self, name, transform = None):
        self.name = name
        self.transform = transform

    def _on_output_ready(self, output):
        for transformInput in self.transform.inputs:
            if transformInput.Status != DataTransformStatus.Finished:
                break
        else:
            # all inputs are in a finished state, begin the transform
            self.transfrom.transform()

class DataTransformOutput:
    def _get_name(self):
        return self.__name
    def _set_name(self, name):
        self.__name = name
    def _get_transform(self):
        return self.__transform
    def _set_transform(self, transform):
        self.__transform = transform
        if (not transform == None):
            transform.Inputs[self.Name] = this
    
    def _get_value(self):
        return self.__value
    def _set_value(self, value):
        self.__value = value
    def _get_registered_inputs(self):
        return self.__registered_inputs
    
    name = property(_get_name, _set_name)
    transform = property(_get_transform, _set_transform)
    value = property(_get_value, _set_value)
    registered_inputs = property(_get_registered_inputs)
    
    def __init__(self, name, transform = None):
        self.name = name
        self.transform = transform
        self.value = None
        self.__registered_inputs = list()

    def _register(self, input):
        self.registered_inputs.append(input)
        
    def _unregister(self, input):
        while self.registered_inputs.count(input) > 0:
            self.registered_inputs.remove(input)